﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Hrm.Core
{
    public class ExpressionEngine
    {
        //public IQueryable<TEntity> Find<TEntity>(TEntity obj) where TEntity : class
        //{
        //    //获得所有property的信息
        //    PropertyInfo[] properties = obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
        //    //构造初始的query
        //    IQueryable<TEntity> query = this.GetTable<TEntity>().AsQueryable<TEntity>();
        //    //遍历每个property
        //    foreach (PropertyInfo p in properties)
        //    {
        //        if (p != null)
        //        {
        //            Type t = p.PropertyType;
        //            //加入object，Binary，和XDocument， 支持sql_variant，imager 和xml等的影射。
        //            if (t.IsValueType || t == typeof(string) || t == typeof(System.Byte[])
        //                || t == typeof(object) || t == typeof(System.Xml.Linq.XDocument))
        //            {
        //                //如果不为null才算做条件
        //                if (p.GetValue(obj, null) != null)
        //                {
        //                    ParameterExpression param = Expression.Parameter(typeof(TEntity), "c");
        //                    Expression right = Expression.Constant(p.GetValue(obj, null));
        //                    Expression left = Expression.Property(param, p.Name);
        //                    Expression filter = Expression.Equal(left, right);

        //                    Expression<Func<TEntity, bool>> pred = Expression.Lambda<Func<TEntity, bool>>( filter, param);
        //                    query = query.Where(pred);
        //                }
        //            }
        //        }
        //    }
        //    return query;
        //}
        List<Person> persons = new List<Person>
        {
            new  Person  { Name = "Flamur" , Surname = "Dauti" ,    Age = 39,
                           City = "Prishtine" , IsHomeOwner = true ,  Salary = 12000.0 },
            new  Person  { Name = "Blerta" , Surname = "Frasheri" , Age = 25,
                           City = "Mitrovice" , IsHomeOwner = false , Salary = 9000.0 },
            new  Person  { Name = "Berat" ,  Surname = "Dajti" ,    Age = 45,
                           City = "Peje" ,      IsHomeOwner = true ,  Salary = 10000.0 },
            new  Person  { Name = "Laura" ,  Surname = "Morina" ,   Age = 23,
                           City = "Mitrovice" , IsHomeOwner = true ,  Salary = 25000.0 },
            new  Person  { Name = "Olti" ,   Surname = "Kodra" ,    Age = 19,
                           City = "Prishtine" , IsHomeOwner = false , Salary = 8000.0 },
            new  Person  { Name = "Xhenis" , Surname = "Berisha" ,  Age = 26,
                           City = "Gjakove" ,   IsHomeOwner = false , Salary = 7000.0 },
            new  Person  { Name = "Fatos" ,  Surname = "Gashi" ,    Age = 32,
                           City = "Peje" ,      IsHomeOwner = true ,  Salary = 6000.0 },
        };

        public IList<Person> FilterByName(string name)
        {
            return persons.Where(p => p.Name == name).ToList();
        }

        public IList<Person> FilterBySurname(string surname)
        {
            return persons.Where(p => p.Surname == surname).ToList();
        }

        public IList<Person> FilterByNameAndSurname(string name, string surname)
        {
            return persons.Where(p => p.Name == name && p.Surname == surname).ToList();
        }

        public void Main()
        {
            List<Filter> filter = new List<Filter>()
            {
                new Filter { PropertyName = "City" ,Operation = Op.Equals, Value = "Mitrovice"  },
                new Filter { PropertyName = "Name" ,Operation = Op.StartsWith, Value = "L"  },
                new Filter { PropertyName = "Salary" ,Operation = Op.GreaterThan, Value = 9000.0 }
            };

            var deleg = ExpressionBuilder.GetExpression<Person>(filter).Compile();
            var filteredCollection = persons.Where(deleg).ToList();
        }
 
    }
    public enum Op
    {
        Equals,
        GreaterThan,
        LessThan,
        GreaterThanOrEqual,
        LessThanOrEqual,
        Contains,
        StartsWith,
        EndsWith
    }
    public class Filter
    {
        public string PropertyName { get; set; }
        public Op Operation { get; set; }
        public object Value { get; set; }
    }
    public static class ExpressionBuilder
    {
        private static MethodInfo containsMethod = typeof(string).GetMethod("Contains");
        private static MethodInfo startsWithMethod = typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) });
        private static MethodInfo endsWithMethod = typeof(string).GetMethod("EndsWith", new Type[] { typeof(string) });


        public static Expression<Func<T,bool>> GetExpression<T>(IList<Filter> filters)
        {
            if (filters.Count == 0)
                return null;

            ParameterExpression param = Expression.Parameter(typeof(T), "t");
            Expression exp = null;

            if (filters.Count == 1)
                exp = GetExpression<T>(param, filters[0]);
            else if (filters.Count == 2)
                exp = GetExpression<T>(param, filters[0], filters[1]);
            else
            {
                while (filters.Count > 0)
                {
                    var f1 = filters[0];
                    var f2 = filters[1];

                    if (exp == null)
                        exp = GetExpression<T>(param, filters[0], filters[1]);
                    else
                        exp = Expression.AndAlso(exp, GetExpression<T>(param, filters[0], filters[1]));

                    filters.Remove(f1);
                    filters.Remove(f2);

                    if (filters.Count == 1)
                    {
                        exp = Expression.AndAlso(exp, GetExpression<T>(param, filters[0]));
                        filters.RemoveAt(0);
                    }
                }
            }

            return Expression.Lambda<Func<T, bool>>(exp, param);
        }

        private static Expression GetExpression<T>(ParameterExpression param, Filter filter)
        {
            MemberExpression member = Expression.Property(param, filter.PropertyName);
            ConstantExpression constant = Expression.Constant(filter.Value);

            switch (filter.Operation)
            {
                case Op.Equals:
                    return Expression.Equal(member, constant);

                case Op.GreaterThan:
                    return Expression.GreaterThan(member, constant);

                case Op.GreaterThanOrEqual:
                    return Expression.GreaterThanOrEqual(member, constant);

                case Op.LessThan:
                    return Expression.LessThan(member, constant);

                case Op.LessThanOrEqual:
                    return Expression.LessThanOrEqual(member, constant);

                case Op.Contains:
                    return Expression.Call(member, containsMethod, constant);

                case Op.StartsWith:
                    return Expression.Call(member, startsWithMethod, constant);

                case Op.EndsWith:
                    return Expression.Call(member, endsWithMethod, constant);
            }

            return null;
        }

        private static BinaryExpression GetExpression<T>(ParameterExpression param, Filter filter1, Filter filter2)
        {
            Expression bin1 = GetExpression<T>(param, filter1);
            Expression bin2 = GetExpression<T>(param, filter2);
            return Expression.AndAlso(bin1, bin2);
        }
    }
    

    public class Person
    {
        public string Name { get; set; }
        public string Surname { get; set; }
        public int Age { get; set; }
        public string City { get; set; }
        public double Salary { get; set; }
        public bool IsHomeOwner { get; set; }
    }
}
